import java.awt.Color;
import java.awt.Graphics;
import java.awt.Point;

import javax.swing.JPanel;


public class GraphPanel extends JPanel {
	private static final long serialVersionUID = 1L;
	final static private double LINE_SCALAR = 0.9;
	final static private int MARKS_NUMBER = 32;
	private int xOffset = 0;
	private int yOffset = 0;
	private Point[] point;
	private double[] dX = null;
	private double[] dY = null;
	private int pointCount = 0;
	private Point xAxisStart, xAxisEnd;
	private Point yAxisStart, yAxisEnd;
	private String label = "";
	private Point[] xMarks;
	private String[] xMarkString;
	private Point[] yMarks;
	private String[] yMarkString;

	public GraphPanel(int numberOfPoints, int width, int height){
		this(width, height);
		if (numberOfPoints>0){
			dX = new double[numberOfPoints];
			dY = new double[numberOfPoints];
			point = new Point[numberOfPoints];
			for(int i=0; i<point.length; ++i){
				point[i] = new Point(0, 0);
			}
		}
	}
	
	public GraphPanel(int width, int height){
		this.setSize(width, height);
		xOffset = (int) ((double)width * (1 - LINE_SCALAR) * 0.5);
		yOffset = (int) ((double)height * (1 - LINE_SCALAR) * 0.5);
	}
	
	public void drawArrayValues(double[] array){
		//Index is x coordinate
		double stepX =	(double)this.getWidth() / (double)array.length * LINE_SCALAR;
		double minY = Double.MAX_VALUE;
		double maxY = Double.MIN_VALUE;
		for (int i=0; i<array.length; ++i){
			minY = Math.min(minY, array[i]);
			maxY = Math.max(maxY, array[i]);
		}
		double stepY = (double)this.getHeight() / (maxY - minY) * LINE_SCALAR;
		point = new Point[array.length];
		for (int i=0; i<point.length; ++i){
			//Convert the values into coordinates of java
			point[i] = new Point((int)(i * stepX) + xOffset, (int)((maxY - array[i]) * stepY) + yOffset);
		}
		calculateAxisPoints(maxY, stepY);
		calculateAxisMarks(maxY, stepY);
		label = "max: " + maxY;
		this.repaint();
	}
	
	public void drawArrayValues(int[] array, String label){
		//Index is x coordinate
		double stepX =	(double)this.getWidth() / (double)array.length * LINE_SCALAR;
		double minY = Double.MAX_VALUE;
		double maxY = Double.MIN_VALUE;
		for (int i=0; i<array.length; ++i){
			minY = Math.min(minY, (double) array[i]);
			maxY = Math.max(maxY, (double) array[i]);
		}
		double stepY = (double)this.getHeight() / (maxY - minY) * LINE_SCALAR;
		point = new Point[array.length];
		for (int i=0; i<point.length; ++i){
			//Convert the values into coordinates of java
			point[i] = new Point((int)(i * stepX) + xOffset, (int)((maxY - (double)array[i]) * stepY) + yOffset);
		}
		calculateAxisPoints(maxY, stepY);
		calculateAxisMarks(maxY, stepY);
		this.label = label;
		this.repaint();
	}

	public void addNextPoint(double x, double y){
		dX[pointCount] = x;
		dY[pointCount] = y;
		++pointCount;
	}
	
	public void formGraph(){
		//Suppose the x are all in order
		//Analyze the max and min values of x and y
		double stepX =	(double)this.getWidth() / (dX[dX.length-1] - dX[0]) * LINE_SCALAR;
		double minY = Double.MAX_VALUE;
		double maxY = Double.MIN_VALUE;
		for (int i=0; i<dX.length; ++i){
			minY = Math.min(minY, dY[i]);
			maxY = Math.max(maxY, dY[i]);
		}
		double stepY = (double)this.getHeight() / (maxY - minY) * LINE_SCALAR;
		for (int i=0; i<point.length; ++i){
			//Convert the values into coordinates of java
			point[i].x = (int)(dX[i] * stepX) + xOffset;
			point[i].y = (int)((maxY - dY[i]) * stepY) + yOffset;
		}
		calculateAxisPoints(maxY, stepY);
		calculateAxisMarks(maxY, stepY);
		this.repaint();
	}
	
	protected void calculateAxisPoints(double maxY, double stepY){
		xAxisStart = new Point(0, (int) (maxY * stepY + yOffset));
		xAxisEnd = new Point(this.getWidth(), xAxisStart.y);
		yAxisStart = new Point(xOffset, 0);
		yAxisEnd = new Point(xOffset, this.getHeight());
	}
	
	protected void calculateAxisMarks(double maxY, double stepY){
		int xMarkStep = point.length / MARKS_NUMBER;
		xMarks = new Point[MARKS_NUMBER];
		xMarkString = new String[MARKS_NUMBER];
		for(int i=0; i<xMarks.length; ++i){
			xMarks[i] = new Point(point[i * xMarkStep].x, xAxisStart.y+30);
			xMarkString[i] = Integer.toString(i*xMarkStep);
		}
		
		yMarks = new Point[MARKS_NUMBER];
		yMarkString = new String[MARKS_NUMBER];
		int yMarkStringStart = 0;
		int yMarkStringStep = (int) (maxY / MARKS_NUMBER);
		int yMarkPositionStart = xAxisStart.y;
		for(int i=0; i<yMarks.length; ++i){
			yMarks[i] = new Point(xOffset/2-5, yMarkPositionStart);
			yMarkPositionStart -= stepY * yMarkStringStep;
			yMarkString[i] = Integer.toString(yMarkStringStart);
			yMarkStringStart += yMarkStringStep;
		}
	}
	
	@Override
	protected void paintComponent(Graphics g) {
		// TODO Auto-generated method stub
		super.paintComponent(g);
		setBackground(Color.white);

		//Draw the coordinate axis
		if(xAxisStart != null){
			g.drawLine(xAxisStart.x, xAxisStart.y, xAxisEnd.x, xAxisEnd.y);
			g.drawLine(yAxisStart.x, yAxisStart.y, yAxisEnd.x, yAxisEnd.y);
		}
		
		//Draw the marks
		if(xMarks != null){
			for(int i=0; i<xMarks.length; ++i){
				g.drawString(xMarkString[i], xMarks[i].x, xMarks[i].y);
			}
		}
		if(yMarks != null){
			for(int i=0; i<yMarks.length; ++i){
				g.drawString(yMarkString[i], yMarks[i].x, yMarks[i].y);
			}
		}
		
		if(label.length()>0){
			g.drawString(label, this.getWidth()-50, this.getHeight()-80);
		}

		g.setColor(Color.BLUE);
		if(point != null){
			for(int i=0; i<point.length-1; ++i){
				g.drawLine(point[i].x, point[i].y, point[i+1].x, point[i+1].y);
			}
		}
	}
	
}
